<?php
/**
 * @package if
 * @subpackage module.smartyview
 */

if_compilable(__FILE__);
define("SMARTY_VIEW_FUNCTIONS_LOADED", 1);

/**
 * 显示控制器的错误
 * 
 * - {{if_errors param=name}}
 * - {{if_errors param=name class=error_style_class}}
 * - {{if_errors}}
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_errors($params, &$smarty) {
	$controller = $smarty->get_template_vars("_controller");
	$errors = array();
	if (isset($params["param"])) {
		$paramErrors = $controller->errors($params["param"]);
		if (!empty($paramErrors)) {
			$errors = array($params["param"] => $paramErrors);
		}
	}
	else {
		$errors = $controller->errors();
	}
	if (!empty($errors)) {
		$class = isset($params["class"])?$params["class"]:"if_error";
		$ret = "<ul class=\"{$class}\">";
		foreach ($errors as $param => $_errors) {
			foreach ($_errors as $_error) {
				$ret .= "<li class=\"{$class}_{$param}\">" . $_error . "</li>";
			}
		}
		$ret .= "</ul>";
		return $ret;
	}
}

/**
 * __URI__常量
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @since 1.0
 * @return string
 */
function if_smarty_func___URI__($params, &$smarty) {
	return __URI__;
}

/**
 * __BASE__常量
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @since 1.0
 * @return string
 */
function if_smarty_func___BASE__($params, &$smarty) {
	return __BASE__;
}

/**
 * __ROUTER__常量
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @since 1.0
 * @return string
 */
function if_smarty_func___ROUTER__($params, &$smarty) {
	return __ROUTER__;
}

/**
 * __CONTROLLER__常量
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func___CONTROLLER__($params, &$smarty) {
	return __CONTROLLER__;
}

/**
 * __ACTION__常量
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func___ACTION__($params, &$smarty) {
	return __ACTION__;
}

/**
 * 取得一个动作的URL
 * 
 * - {{if_url controller=hello action=world}}
 * - {{if_url action=world}}
 * - {{if_url action=world param1=value1 param2=value2}}
 * 
 * 只有通过路由访问的控制器才能使用该函数
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_url($params, &$smarty) {
	$controllerObj = $smarty->get_template_vars("_controller");
	$controller = null;
	$action = null;
	if (isset($params["controller"])) {
		$controller = $params["controller"];
		unset($params["controller"]);
	}
	if (isset($params["action"])) {
		$action = $params["action"];
		unset($params["action"]);
	}
	else {
		$action = IController::defaultAction();
	}
	
	return $controllerObj->path($controller, $action, $params);
}

/**
 * 生成option列表
 * 
 * - {{if_options options=xx}}
 * - {{if_options options=xx value_key=id text_key=name}}
 * - {{if_options model=TUser}}
 * - {{if_options model=TUser [cond|size|offset|limit|pk|desc|asc]=xx}}
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_options($params, &$smarty) {
	if (isset($params["options"]) && is_array($params["options"])) {
		$options = $params["options"];
		if (isset($params["value_key"]) && isset($params["text_key"])) {
			$params["options"] = if_array_combine($options, $params["value_key"], $params["text_key"]);
		}
	}
	
	if_smarty_func_parse_params($params);

	if (!function_exists("smarty_function_html_options")) {
		require $smarty->_get_plugin_filepath("function", "html_options"); 
	}
	return smarty_function_html_options($params, $smarty);
}

/**
 * 分析Smarty中函数参数
 *
 * @param array $params 参数
 * @since 1.0
 */
function if_smarty_func_parse_params(&$params) {
	if (isset($params["model"])) {
		$dao = IDAO::dao($params["model"]);
		$query = $dao->query($params["model"]);
		$functions = array("cond", "size", "offset", "limit", "pk", "desc", "asc");
		foreach ($functions as $func) {
			if (isset($params[$func])) {
				$query->$func($params[$func]);
			}
		}
		$options = $query->findAll();
		if (!isset($params["value_key"])) $params["value_key"] = $dao->pkName();
		if (!isset($params["text_key"])) $params["text_key"] = "name";
		$params["options"] = if_array_combine($options, $params["value_key"], $params["text_key"]);
		foreach ($functions as $func) {
			if (isset($params[$func])) {
				unset($params[$func]);
			}
		}
		unset($params["model"]);
	}
	if (isset($params["value_key"])) {
		unset($params["value_key"]);
	}
	if (isset($params["text_key"])) {
		unset($params["text_key"]);
	}
}

function if_smarty_func_if_options_range($params, &$smarty) {
	if (isset($params["from"]) && isset($params["to"])) {
		$options = array();
		if (isset($params["step"])) {
			$options = range($params["from"], $params["to"], $params["step"]);
		}
		else {
			$options = range($params["from"], $params["to"]);
		}
		if (isset($params["reverse"]) && $params["reverse"]) {
			$options = array_reverse($options);
		}
		if (!isset($params["options"]) || !is_array($params["options"])) {
			$params["options"] = array();
		}
		foreach ($options as $option) {
			$params["options"][$option] = $option;
		}
	}
	return if_smarty_func_if_options($params, $smarty);
}

function if_smarty_func_if_options_years($params, &$smarty) {
	$params["from"] = date("Y", strtotime("-100 years"));
	$params["to"] = date("Y", strtotime("+1 Year"));
	return if_smarty_func_if_options_range($params, $smarty);
}

function if_smarty_func_if_options_months($params, &$smarty) {
	$params["from"] = 1;
	$params["to"] = 12;
	return if_smarty_func_if_options_range($params, $smarty);
}

function if_smarty_func_if_options_days($params, &$smarty) {
	$params["from"] = 1;
	$params["to"] = 31;
	return if_smarty_func_if_options_range($params, $smarty);
}

function if_smarty_func_if_options_countries($params, &$smarty) {
	$countries = o("@global.countries");
	$params["options"] = $countries;
	return if_smarty_func_if_options($params, $smarty);
}

function if_smarty_func_if_options_months_full($params, &$smarty) {
	$locale = ILocale::current()->name();
	$months = o("@global.date_months_full");
	$options = array();
	if (isset($months[$locale])) {
		$options = $months[$locale];
	}
	else {
		$options = array_shift($months);
	}
	$params["options"] = $options;
	return if_smarty_func_if_options($params, $smarty);
}

function if_smarty_func_if_options_months_short($params, &$smarty) {
	$locale = ILocale::current()->name();
	$months = o("@global.date_months_short");
	$options = array();
	if (isset($months[$locale])) {
		$options = $months[$locale];
	}
	else {
		$options = array_shift($months);
	}
	$params["options"] = $options;
	return if_smarty_func_if_options($params, $smarty);
}

/**
 * 生成一组checkbox
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_checkboxes($params, &$smarty) {
	if (isset($params["options"]) && is_array($params["options"])) {
		$options = $params["options"];
		if (isset($params["value_key"]) && isset($params["label_key"])) {
			$params["options"] = array();
			foreach ($options as $key => $value) {
				$params["options"][@$value[$params["value_key"]]] = @$value[$params["label_key"]];
			}
		}
	}
	
	if_smarty_func_parse_params($params);
	
	if (!function_exists("smarty_function_html_checkboxes")) {
		require $smarty->_get_plugin_filepath("function", "html_checkboxes"); 
	}
	return smarty_function_html_checkboxes($params, $smarty);
}

/**
 * 生成一组checkbox
 * 
 * 同 if_smarty_func_if_checkboxes
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 * @see if_smarty_func_if_checkboxes
 */
function if_smarty_func_if_html_checkboxes($params, &$smarty) {
	return if_smarty_func_if_checkboxes($params, $smarty);
}

/**
 * 生成一组radio
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_radios($params, &$smarty) {
	if (isset($params["options"]) && is_array($params["options"])) {
		$options = $params["options"];
		if (isset($params["value_key"]) && isset($params["label_key"])) {
			$params["options"] = array();
			foreach ($options as $key => $value) {
				$params["options"][@$value[$params["value_key"]]] = @$value[$params["label_key"]];
			}
		}
	}
	if_smarty_func_parse_params($params);
	if (!function_exists("smarty_function_html_radios")) {
		require $smarty->_get_plugin_filepath("function", "html_radios"); 
	}
	return smarty_function_html_radios($params, $smarty);
}

/**
 * 取得context中的值
 *
 * - {{if_x param="id"}}
 * - {{if_x param="#login", "escape"=>"..", "charset" => ".."}}
 * - ...
 * 
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return mixed
 * @since 1.0
 */
function if_smarty_func_if_x($params, &$smarty) {
	if (isset($params["param"])) {
		$x = x($params["param"]);
		if (is_scalar($x) && isset($params["escape"])) {
			if (!function_exists("smarty_modifier_escape")) {
				require $smarty->_get_plugin_filepath("modifier", "escape");
			}
			if (isset($params["charset"])) {
				$x = smarty_modifier_escape($x, $params["escape"], $params["charset"]);
			}
			else {
				$x = smarty_modifier_escape($x, $params["escape"]);
			}
		}
		if (!is_scalar($x) && !is_null($x)) {
			p($x);
			return;
		}
		return $x;
	}
	return null;
}

/**
 * 动态区块
 * 
 * 当caching设为1时，可以用
 *   {{if_dynamic}}
 *   ...
 *   {{/if_dynamic}}
 * 指定动态的部分
 * 
 * @param array $params 参数
 * @param string $content 区块
 * @param Smarty $smarty 模板对象
 * @return string
 */
function if_smarty_block_if_dynamic($params, $content, &$smarty) {
    return $content;
}

/**
 * 取得配置中的选项值
 * 
 * - {{if_o param=@db.default.host}}
 *
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return mixed
 * @since 1.0
 */
function if_smarty_func_if_o($params, &$smarty) {
	if (isset($params["param"])) {
		return o($params["param"]);
	}
}

/**
 * 取得本地化的消息
 * 
 * - {{if_m param=button}}
 * - {{if_m param=button value="Submit"}}
 * 
 * @param array $params 参数
 * @param Smarty $smarty Smarty模板对象
 * @return string
 * @since 1.0
 */
function if_smarty_func_if_m($params, &$smarty) {
	if (isset($params["param"])) {
		$param = $params["param"];
		unset($params["param"]);
		return m($param, $params);
	}
	return null;
}

/**
 * 用于处理UI控件
 * 
 * @param string $source 模板源码
 * @param Smarty_Compiler $smarty Smarty_Compiler
 * @return string
 * @since 1.1.0
 */
function if_smarty_prefilter_ui($source, &$smarty) {
	//v1.0.3: 过滤 {{[~!@#%*]var_name}}
	$source = preg_replace("/(" . preg_quote($smarty->left_delimiter, "/") . ")\s*([!#*%@~][\w\.\-]*)\s*("  . preg_quote($smarty->right_delimiter, "/") . ")/s", "\\1if_x param=\"\\2\"\\3", $source);
	
	//v1.0.3: 处理UI控件
	$GLOBALS["SMARTY_LEFT_DELIM"] = $smarty->left_delimiter;
	$GLOBALS["SMARTY_RIGHT_DELIM"] = $smarty->right_delimiter;
	
	//处理注释v1.1.0
	$source = preg_replace("/<!-{3}.*-{2}>/sU", "", $source);

	$source = preg_replace_callback("/<(\w+:\w+)((\s+([^>]|(?<=\\\)>)*)?)\s*\/(?<!(\\\))>/sU", create_function('$match','
		$name = $match[1];
		list($prefix, $tagName) = explode(":", $name);
		$match[2] = str_replace("\>", ">", $match[2]);
		if ($prefix == "c" || $prefix == "C") {
			return "{$GLOBALS["SMARTY_LEFT_DELIM"]}{$tagName}{$match[2]}{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
		}
		$class = UIControl::classByName($name);
		if (!$class) {
			return $match[0];
		}
		$order = UIContainer::instance()->maxOrder();
		return "{$GLOBALS["SMARTY_LEFT_DELIM"]}ui_control control.name=\"{$name}\"{$match[2]} control.class=\"{$class}\" control.order={$order}{$GLOBALS["SMARTY_RIGHT_DELIM"]}{$GLOBALS["SMARTY_LEFT_DELIM"]}/ui_control{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
	'), $source);
	$source = preg_replace_callback("/<(\w+:\w+)((\s+.*)?)(?<!\\\)>/sU", create_function('$match','
		$name = $match[1];
		list($prefix, $tagName) = explode(":", $name);
		$match[2] = str_replace("\>", ">", $match[2]);
		if ($prefix == "c" || $prefix == "C") {
			return "{$GLOBALS["SMARTY_LEFT_DELIM"]}{$tagName}{$match[2]}{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
		}
		$class = UIControl::classByName($name);
		if (!$class) {
			return $match[0];
		}
		$order = UIContainer::instance()->maxOrder();
		if (!isset($GLOBALS["UI_CONTROL_BEGIN"])) {
			$GLOBALS["UI_CONTROL_BEGIN"] = array();
		}
		if (!isset($GLOBALS["UI_CONTROL_BEGIN"][$name])) {
			$GLOBALS["UI_CONTROL_BEGIN"][$name] = array();
		}
		$GLOBALS["UI_CONTROL_BEGIN"][$name][] = $match[0];
		return "{$GLOBALS["SMARTY_LEFT_DELIM"]}ui_control control.name=\"{$name}\"{$match[2]} control.class=\"{$class}\" control.order={$order}{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
	'), $source);
	$source = preg_replace_callback("/<\/(\w+:\w+)>/U", create_function('$match','
		$name = $match[1];
		list($prefix, $tagName) = explode(":", $name);
		if ($prefix == "c" || $prefix == "C") {
			return "{$GLOBALS["SMARTY_LEFT_DELIM"]}/{$tagName}{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
		}		
		$class = UIControl::classByName($name);
		if (!$class) {
			return $match[0];
		}
		if (!isset($GLOBALS["UI_CONTROL_END"])) {
			$GLOBALS["UI_CONTROL_END"] = array();
		}
		if (!isset($GLOBALS["UI_CONTROL_END"][$name])) {
			$GLOBALS["UI_CONTROL_END"][$name] = array();
		}
		$GLOBALS["UI_CONTROL_END"][$name][] = $match[0];
		return "{$GLOBALS["SMARTY_LEFT_DELIM"]}/ui_control{$GLOBALS["SMARTY_RIGHT_DELIM"]}";
	'), $source);
	
	//判断控件是否有闭合
	$errorBeginDefs = array();
	$errorEndDefs = array();
	if (isset($GLOBALS["UI_CONTROL_BEGIN"])) {
		foreach ($GLOBALS["UI_CONTROL_BEGIN"] as $name => $defs) {
			if (!isset($GLOBALS["UI_CONTROL_END"][$name])) {
				$errorBeginDefs = if_array_join($errorBeginDefs, $defs);
			}
			else {
				$beginCount = count($defs);
				$endCount = count($GLOBALS["UI_CONTROL_END"][$name]);
				if ($beginCount > $endCount) {
					$errorBeginDefs = if_array_join($errorBeginDefs, array_slice($defs, $beginCount - 1));
				}
				elseif ($beginCount < $endCount) {
					$errorEndDefs = if_array_join($errorEndDefs, array_slice($GLOBALS["UI_CONTROL_END"][$name], $endCount - 1));
				}
			}
		}
	}
	if (isset($GLOBALS["UI_CONTROL_END"])) {
		foreach($GLOBALS["UI_CONTROL_END"] as $name => $defs) {
			if (!isset($GLOBALS["UI_CONTROL_BEGIN"][$name])) {
				$errorEndDefs = if_array_join($errorEndDefs, $defs);
			}
		}
	}
	if (!empty($errorBeginDefs) || !empty($errorEndDefs)) {
		$error = "<br/>UI control parser error &lt;resource:{$smarty->_current_file}&gt;.<br/>";
		foreach ($errorBeginDefs as $def) {
			$error .= "- " . nl2br(htmlspecialchars($def)) . " has no close tag<br/>\n";
		}
		foreach ($errorEndDefs as $def) {
			$error .= "- " . nl2br(htmlspecialchars($def)) . " has no open tag<br/>\n";
		}
		throw new IException($error, 0x100029);
	}
	
	return $source;
}

/**
 * 在模板中加载UIControl
 * 
 * 参数：
 * - control.id 控件ID，为可选参数
 * - control.name 控件名
 * - control.class 控件类
 * - control.order 控件顺序
 * 
 * @param array $params 控件参数
 * @param Smarty $smarty Smarty模板对象
 * @param boolean $init 是否为初始化
 * @return string
 * @since 1.0.3
 */
function if_smarty_block_ui_control($params, $content, &$smarty, $init = false) {
	//重新组织
	$newParams = ISmartyView::parseSmartyParams($params);
	$options = @$newParams[UIControl::OPTION_KEYWORD];
	if (!isset($options["name"])) {
		return "unknown control";
	}
	
	//处理close tag
	if (!$init) {
		if (isset($options["id"])) {
			$control = UIContainer::instance()->get($options["id"]);
			if ($control) {
				$control->setContent($content);
				$control->_before_render();
				$string = $control->render();
				$string = str_replace("\${yield}", $content, $string);	
				$control->_after_render();
				return $string;
			}
		}
	}
	
	//以下是处理open tag
	
	//取得父控件
	$lastBlockIndex = count($smarty->_tag_stack) - 1;
	$idProp = UIControl::OPTION_KEYWORD . ".id";
	$parentControl = null;
	if ($lastBlockIndex > 0) {
		$parentBlockIndex = $lastBlockIndex - 1;
		$parentId = $smarty->_tag_stack[$parentBlockIndex][1][$idProp];
		$parentControl = UIContainer::instance()->get($parentId);
	}
	else {//顶级
		$parentControl = UIPage::instance();
	}	
	
	//控件选项
	if (!isset($options["id"])) {
		if (isset($newParams["id"]) && $newParams["id"]) {
			$options["id"] = $newParams["id"];
		}
		else {
			//为了防止在foreach和loop语句中有多个相同control.order的控件，我们需要知道order有没有被用掉
			if (UIContainer::instance()->getByOrder($options["order"])) {
				$options["order"] = UIContainer::instance()->maxOrder();
			}
			$options["id"] = $parentControl->id() . "_" . $options["name"] . "_" . $options["order"];
		}
	}
	$smarty->_tag_stack[$lastBlockIndex][1][$idProp] = $options["id"];//当前控件ID
	
	$controlName = $options["name"];
	$inContainer = true;
	$control = UIContainer::instance()->get($options["id"]);
	if (!$control) {
		$controlClass = UIControl::classByName($controlName);
		if (!$controlClass) {
			return "undefined control '{$controlName}'";
		}
		if (strstr($controlClass, ".")) {
			import($controlClass);
			$controlClass = if_class_name($controlClass);
		}
		$control = new $controlClass($options);
		$control->addToContainer();
	}
	$control->setOptions($options);
	unset($newParams[UIControl::OPTION_KEYWORD]);
	$control->setSmarty($smarty);
	$control->setAttr($newParams);
	$control->setName($controlName);
	$control->setController($smarty->get_template_vars("_controller"));
	$parentControl->addChild($control);
	$control->_after_parse();
	
	return null;
}

/**
 * 在Smarty中设置I-F私有的变量
 *
 * @param Smarty $smarty Smarty对象
 * @param string $name 变量名
 * @param string $value 变量值
 * @since 1.1.0
 */
function if_smarty_set_var(&$smarty, $name, $value) {
	if (!is_array($smarty->IF_VARS)) {
		$smarty->IF_VARS = array();
	}
	$smarty->IF_VARS = if_array_set($smarty->IF_VARS, $name, $value);
}

/**
 * 获取在Smarty中设置的I-F私有的变量
 *
 * @param Smarty $smarty Smarty对象
 * @param string $name 变量名
 * @return mixed
 * @since 1.1.0
 */
function if_smarty_get_var(&$smarty, $name) {
	if (!is_array($smarty->IF_VARS)) {
		return;
	}
	return if_array_get($smarty->IF_VARS, $name);
}

?>